package com.xishu.service;

import com.xishu.entity.order.OrderNo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.ObjectOptimisticLockingFailureException;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;

public class OrderNoService {
    private static Logger logger = LoggerFactory.getLogger(OrderNoService.class);
    private static OrderNoService instance = new OrderNoService();
    private CommonService commonService = CommonService.getInstance();
    private static final int START_NO = 100000;
    private AtomicLong orderItemNo = new AtomicLong();
    private Map<Long, Object> shopLock = new HashMap<>();

    private OrderNoService() {
    }

    public static OrderNoService getInstance() {
        return instance;
    }

    public void setOrderItemNo(AtomicLong orderItemNo) {
        this.orderItemNo = orderItemNo;
    }

    public synchronized Object getLockObject(Long shopId) {
        Object lock = shopLock.get(shopId);

        if (lock == null) {
            Object o = new Object();
            shopLock.put(shopId, o);
            return o;
        }

        return lock;
    }

    /**
     * 获取订单编号
     *
     * @param shopId
     * @return
     * @throws Exception
     */
    public int getOrderNo(Long shopId) throws Exception {
        logger.info("will get the order no {}", shopId);
        //按照分店并发
        Object lockObject = getLockObject(shopId);

        synchronized (lockObject) {
            logger.info("get the lock {}", shopId);
            OrderNo orderNo = new OrderNo();
            orderNo.setShopId(shopId);
            Optional<OrderNo> orderNoOptional = commonService.searchOneOptional(orderNo);

            //第一次不用考虑并发
            if (!orderNoOptional.isPresent()) {
                orderNo.setOrderNo(1);
                orderNo.setLockVersion(1);
                commonService.createObject(orderNo);
                return 1;
            }

            OrderNo orderNoInDb = orderNoOptional.get();
            orderNoInDb.setOrderNo(orderNoInDb.getOrderNo() + 1);

            try {
                commonService.save(orderNoInDb);
                logger.info("new order no {} for {}", orderNoInDb.getOrderNo(), shopId);
            } catch (ObjectOptimisticLockingFailureException e) {
                //重试
                return getOrderNo(shopId);
            } catch (Exception e) {
                return getOrderNo(shopId);
            }

            logger.info("success get the order no");
            return orderNoInDb.getOrderNo();
        }
    }

    /**
     * 获取订单里面的菜的ID
     * <p>
     * 直接采用数据库的序列
     *
     * @return
     */
    public long getOrderItemNo() {
        long startTime = System.currentTimeMillis();
        long newOrderItemNo = orderItemNo.addAndGet(1);
        logger.info("order item no {} cost time is {}", newOrderItemNo, System.currentTimeMillis() - startTime);
        return newOrderItemNo;
    }

    /**
     * 获取餐桌编号
     *
     * @param shopId
     * @return
     */
    public int getFoodTableNo(Long shopId) throws Exception {
        //按照分店并发
        Object lockObject = getLockObject(shopId);

        synchronized (lockObject) {
            OrderNo orderNo = new OrderNo();
            orderNo.setShopId(shopId);
            Optional<OrderNo> orderNoOptional = commonService.searchOneOptional(orderNo);

            //第一次不用考虑并发
            if (!orderNoOptional.isPresent()) {
                //默认从10万起，以避免跟以前的冲突了
                orderNo.setFoodTableNo(START_NO);
                orderNo.setLockVersion(1);
                commonService.createObject(orderNo);
                return 1;
            }

            OrderNo orderNoInDb = orderNoOptional.get();
            orderNoInDb.setFoodTableNo(orderNoInDb.getFoodTableNo() + 1);

            if (orderNoInDb.getFoodTableNo() < START_NO) {
                orderNoInDb.setFoodTableNo(START_NO);
            }

            try {
                commonService.save(orderNoInDb);
            } catch (ObjectOptimisticLockingFailureException e) {
                //重试
                return getFoodTableNo(shopId);
            }

            return orderNoInDb.getFoodTableNo();
        }
    }

    /**
     * 生成聚餐码
     *
     * @param shopId
     * @return
     */
    public int getTakeOutNo(Long shopId) throws Exception {
        OrderNo orderNo = new OrderNo();
        orderNo.setShopId(shopId);
        Optional<OrderNo> orderNoOptional = commonService.searchOneOptional(orderNo);

        //第一次不用考虑并发
        if (!orderNoOptional.isPresent()) {
            //默认从10万起，以避免跟以前的冲突了
            orderNo.setTakeOutNo(1);
            orderNo.setLockVersion(1);
            commonService.createObject(orderNo);
            return 1;
        }

        OrderNo orderNoInDb = orderNoOptional.get();
        orderNoInDb.setTakeOutNo(orderNoInDb.getTakeOutNo() + 1);

        try {
            commonService.save(orderNoInDb);
        } catch (ObjectOptimisticLockingFailureException e) {
            //重试
            return getTakeOutNo(shopId);
        }

        return orderNoInDb.getTakeOutNo();
    }

    /**
     * 重置订单编号
     *
     * @param shopId
     * @return
     */
    public void resetOrderNo(Long shopId) throws Exception {
        OrderNo orderNo = new OrderNo();
        orderNo.setShopId(shopId);
        Optional<OrderNo> orderNoOptional = commonService.searchOneOptional(orderNo);
        if (!orderNoOptional.isPresent()) {
            logger.info("can not find the order no");
            return;
        }

        OrderNo orderNoInDb = orderNoOptional.get();
        orderNoInDb.setOrderNo(0);
        orderNoInDb.setTakeOutNo(0);

        try {
            commonService.save(orderNoInDb);
        } catch (ObjectOptimisticLockingFailureException e) {
            //重试
            resetOrderNo(shopId);
        }
    }

    /**
     * 生成闭店序列号，也就是分店的一个经营周期
     *
     * @param shopId
     */
    public int getTimeFix(Long shopId) throws Exception {
        OrderNo orderNo = new OrderNo();
        orderNo.setShopId(shopId);
        Optional<OrderNo> orderNoOptional = commonService.searchOneOptional(orderNo);

        //第一次不用考虑并发
        if (!orderNoOptional.isPresent()) {
            //默认从10万起，以避免跟以前的冲突了
            orderNo.setFoodTableNo(START_NO);
            orderNo.setLockVersion(1);
            commonService.createObject(orderNo);
            return 1;
        }

        OrderNo orderNoInDb = orderNoOptional.get();
        orderNoInDb.setTimeFix(orderNoInDb.getTimeFix() + 1);

        try {
            commonService.save(orderNoInDb);
        } catch (ObjectOptimisticLockingFailureException e) {
            //重试
            return getTimeFix(shopId);
        }

        return orderNoInDb.getTimeFix();
    }
}
